package cn.tedu.reflection;
/**本类用于测试反射*/
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class TestReflect1 {
    /**1.通过单元测试方法获取字节码对象与对应资源的名字*/
    @Test
    public void getClazz() throws ClassNotFoundException {
        //获取字节码对象的三种方式
        Class<?> SClass1 = Class.forName("cn.tedu.reflection.Student");
        Class<?> SClass2 = Student.class;
        Class<?> SClass3 = new Student().getClass();
//        SClass1 字节码对象
        System.out.println(SClass1);//class cn.tedu.reflection.Student
        System.out.println(SClass1.getName());//cn.tedu.reflection.Student
        System.out.println(SClass1.getSimpleName());//Student
        System.out.println(SClass1.getPackage());//package cn.tedu.reflection
        System.out.println(SClass1.getPackage().getName());//cn.tedu.reflection
    }
    /**2.通过单元测试方法获取目标类Student的构造方法*/
    @Test
    public void getConstrct() throws ClassNotFoundException {
//        Class<?> Clazz = Class.forName("cn.tedu.reflection.Student");
        Class<?> clazz = Student.class;
        //获取构造方法们
        Constructor<?>[] cs = clazz.getConstructors();
        //遍历数组获取每个方法
        //for(遍历得到每个元素的类型 遍历得到的元素名:要遍历的元素){循环体;}
        System.out.println("****************");
        for (Constructor<?> c:cs){
            System.out.println(c.getName());
            Class<?>[] p = c.getParameterTypes();
            System.out.println(Arrays.toString(p));
        }
//        System.out.println(Arrays.toString(cs));
    }
    /**3.通过单元测试方法获取目标类Student的普通方法*/
    @Test
    public void getFunction() throws ClassNotFoundException {
//        Class<Student> clazz = Student.class;
        Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
        Method[] methods = clazz.getMethods();
        for(Method m:methods ){
            String name = m.getName();
            System.out.println(name);
            Class<?>[] pt = m.getParameterTypes();
            System.out.println(Arrays.toString(pt));
        }
    }
    /**4.通过单元测试方法获取目标类Student的成员变量*/
    @Test
    public void gerFields(){
        Class<?> clazz=new Student().getClass();
        Field[] fs = clazz.getFields();
        /**注意!!!目前成员变量的修饰符必须是public才能被获取到
         * 默认的修饰符是获取不到的,有坑*/
        for (Field f:fs){
            String name = f.getName();
            System.out.println(name);
            Class<?> type = f.getType();
            System.out.println(type.getName());
        }
    }
    /**5.通过单元测试方法创建目标类对象
     * 方式一:直接通过字节码对象调用newInstance()触发无参构造来创建对象
     * 方式二:我们想用其他的构造函数来创建对象--前提
     * 先得获取指定参数的构造方法,再通过对应的构造方法对象调用newInstance()
     * 注意:调用时需要传参,参数类型是字节码对象,比如String.class, int.class*/
    @Test
    public void getObject() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz = Student.class;
        //2.1创建对象,触发的是无参构造来创建目标类对象
        Object obj = clazz.newInstance();
        System.out.println(obj);
        /**注意:指定构造函数的参数类型时,传入的是字节码对象!!!*/
        //2.2创建对象,想触发对应参数的构造函数来创建对象,就需要先获取到这个指定的构造函数
        Constructor<?> c = clazz.getConstructor(String.class, int.class);
        //通过我们刚刚获取到构造函数来创建目标类的对象[创建对象+属性赋值]
        Object obj2 = c.newInstance("张大彪", 25);
        System.out.println(obj2);
        //
        /**为什么要把obj2强制类型转化为Student类型?
         * 多态中,父类不可以直接使用子类的特有的属性与功能
         * 所以需要把父类型Object强转为子类型Student*/
        Student s=(Student) obj2;
        System.out.println(s.name);
        System.out.println(s.age);
        s.study(5);

    }
}
